// -*- c -*-
//
// %CopyrightBegin%
//
// SPDX-License-Identifier: Apache-2.0
//
// Copyright Ericsson AB 2020-2025. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// %CopyrightEnd%
//

pred.never() {
    return 0;
}

// Test whether a jump table can be used.
pred.use_jump_tab(Size, Rest, MinSize) {
    Sint min, max;
    Sint i;

    if (Size.val < 2 * MinSize || Size.val % 2 != 0) {
        return 0;
    }

    if (Rest[0].type != TAG_i || Rest[1].type != TAG_f) {
        /* Atoms. Can't use a jump table. */
        return 0;
    }

    min = max = Rest[0].val;
    for (i = 2; i < Size.val; i += 2) {
        if (Rest[i].type != TAG_i || Rest[i+1].type != TAG_f) {
            return 0;
        }
        if (Rest[i].val < min) {
            min = Rest[i].val;
        } else if (max < Rest[i].val) {
            max = Rest[i].val;
        }
    }

    return max - min <= Size.val;
}

// Test whether all values in a table are either floats or bignums.
pred.floats_or_bignums(Size, Rest) {
    int i;

    if (Size.val < 2 || Size.val % 2 != 0) {
        return 0;
    }

    for (i = 0; i < Size.val; i += 2) {
        if (Rest[i].type != TAG_q) {
            return 0;
        }
        if (Rest[i+1].type != TAG_f) {
            return 0;
        }
    }

    return 1;
}


// Test whether all values in a table have a fixed size.
pred.fixed_size_values(Size, Rest) {
    int i;

    if (Size.val < 2 || Size.val % 2 != 0) {
        return 0;
    }

    for (i = 0; i < Size.val; i += 2) {
        if (Rest[i+1].type != TAG_f) {
            return 0;
        }
        switch (Rest[i].type) {
        case TAG_a:
        case TAG_i:
        case TAG_v:
            break;
        case TAG_q:
            return is_float(beamfile_get_literal(&S->beam, Rest[i].val));
        default:
            return 0;
        }
    }

    return 1;
}

// Test whether a table has mixe types.
pred.mixed_types(Size, Rest) {
    int i;
    Uint type;

    if (Size.val < 2 || Size.val % 2 != 0) {
        return 0;
    }

    type = Rest[0].type;
    for (i = 0; i < Size.val; i += 2) {
        if (Rest[i].type != type) {
            return 1;
        }
    }

    return 0;
}

// Test whether Bif is "heavy" and should always go through its export entry.
pred.is_heavy_bif(Bif) {
    BeamFile_ImportEntry *import;
    const Export *export;

    if (Bif.type != TAG_u || Bif.val >= S->beam.imports.count) {
        return 0;
    }

    import = &S->beam.imports.entries[Bif.val];
    export = erts_active_export_entry(import->module,
                                      import->function,
                                      import->arity);

    if (export->bif_number != -1) {
        return bif_table[export->bif_number].kind == BIF_KIND_HEAVY;
    }

    return 0;
}

// Predicate to test whether all of the given new small map keys are literals
pred.is_small_map_literal_keys(Size, Rest) {
    Uint pair_count = Size.val / 2;

    if (pair_count > MAP_SMALL_MAP_LIMIT) {
        return 0;
    }

    /*
     * Operations with non-literals have always only one key.
     */
    if (pair_count != 1) {
        return 1;
    }

    switch (Rest[0].type) {
    case TAG_a:
    case TAG_i:
    case TAG_n:
    case TAG_q:
        return 1;
    default:
        return 0;
    }
}

// Test whether the given literal is an empty map.
pred.is_empty_map(Lit) {
    Eterm term;

    if (Lit.type != TAG_q) {
        return 0;
    }

    term = beamfile_get_literal(&S->beam, Lit.val);
    return is_flatmap(term) && flatmap_get_size(flatmap_val(term)) == 0;
}

// Mark this label. Always succeeds.
pred.smp_mark_target_label(L) {
    ASSERT(L.type == TAG_f);
    S->labels[L.val].looprec_targeted = 1;
    return 1;
}

// Test whether this label was targeted by a loop_rec/2 instruction.
pred.smp_already_locked(L) {
    ASSERT(L.type == TAG_u);
    return S->labels[L.val].looprec_targeted;
}

// Sort map keys. Always succeeds unless the instruction contains
// invalid map keys (in which case loading will fail).
pred.map_key_sort(Size, Rest) {
    return beam_load_map_key_sort(S, Size, Rest);
}

// Test that two operands are distinct.
pred.distinct(Val1, Val2) {
    if (Val1.type != Val2.type) {
        return 1;
    } else if (Val1.type == TAG_x || Val1.type == TAG_y) {
        /* We must not compare the type indices (if any). */
        return (Val1.val & REG_MASK) != (Val2.val & REG_MASK);
    } else if (Val1.type == TAG_n) {
        /* NIL has no associated value. */
        return 0;
    } else {
        return Val1.val != Val2.val;
    }
}

// Test that two operands are equal when disregarding types. Opposite of the
// `distinct` predicate.
pred.equal(Val1, Val2) {
    if (Val1.type != Val2.type) {
        return 0;
    } else if (Val1.type == TAG_x || Val1.type == TAG_y) {
        /* We must not compare the type indices (if any). */
        return (Val1.val & REG_MASK) == (Val2.val & REG_MASK);
    } else if (Val1.type == TAG_n) {
        /* NIL has no associated value. */
        return 1;
    } else {
        return Val1.val == Val2.val;
    }
}
